package compoundDB.core;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import compoundDB.database.DatabaseAccessor;
import compoundDB.database.QueryReader;
import compoundDB.database.container.ColumnField;
import compoundDB.database.container.SearchOption;
import compoundDB.database.enumeration.DataType;
import compoundDB.database.enumeration.SearchOptionType;
import compoundDB.io.IOController;
import compoundDB.io.MoleculeReader;
import compoundDB.io.MoleculeWriter;

/**
 * Implementation of the the GUIController interface. Provides functionality for
 * the gui to access the database and import and export.
 */
public class GUIControllerImpl implements GUIController
{
	/**
	 * The database accessor used to act on the database.
	 */
	private DatabaseAccessor m_dba;

	/**
	 * The query reader used to read results from the last search.
	 */
	private QueryReader m_queryReader;

	/**
	 * The dialog used to display errors to the user.
	 */
	private ErrorUserDialog m_errorDisplay;

	/**
	 * The dialog used to force the user to help match import properties.
	 */
	private ImportUserDialog m_importDisplay;

	/**
	 * The number of compounds to process per loop when importing.
	 */
	private static final int IMPORTS_PER_ITERATION = 100;

	/**
	 * Creates the implementation of the GUIController.
	 * 
	 * @param dba
	 *            The DatabaseAcessor that is required to interface with the
	 *            database.
	 * @throws SQLException
	 *             if an error occurs communicating with the database.
	 */
	public GUIControllerImpl(DatabaseAccessor dba) throws SQLException
	{
		m_dba = dba;
		m_dba.openDatabaseConnection();
	}

	// How are we going to deal with exceptions?
	// Let them fall back to the gui, or intercept in core and
	// show up an observer dialogue or something?

	@Override
	public void addColumn(String name, DataType dataType)
	{
		try
		{
			m_dba.addColumnField(name, dataType);
		}
		catch (SQLException e)
		{
			e.printStackTrace();
			System.out.println(e.getMessage());
			m_errorDisplay.setMessage(e.getMessage());
			m_errorDisplay.display();
		}
	}

	@Override
	public void addCompound(Object[] compound, String[] names)
	{
		if (names == null || names.length <= 0)
		{
			m_errorDisplay.setMessage("Must have at least one name for a compound.");
			m_errorDisplay.display();
			return;
		}

		// Set guid and ownerid to 0 so they can be parsed
		compound[1] = "0";
		compound[2] = "0";
		Compound comp;
		try
		{
			comp = convertArrayToCompound(compound, names, true);
		}
		catch (NumberFormatException e1)
		{
			e1.printStackTrace();
			System.out.println(e1.getMessage());
			m_errorDisplay.setMessage(e1.getMessage());
			m_errorDisplay.display();
			return;
		}
		catch (SQLException e1)
		{
			e1.printStackTrace();
			System.out.println(e1.getMessage());
			m_errorDisplay.setMessage(e1.getMessage());
			m_errorDisplay.display();
			return;
		}
		comp.setGUID(0);
		comp.setOwnerID(0);
		List<Compound> comps = new ArrayList<Compound>();
		comps.add(comp);

		try
		{
			m_dba.addCompounds(comps);
		}
		catch (SQLException e)
		{
			e.printStackTrace();
			System.out.println(e.getMessage());
			m_errorDisplay.setMessage(e.getMessage());
			m_errorDisplay.display();
		}
	}

	@Override
	public void advancedSearch(List<SearchOption> filters)
	{
		finishReader();

		try
		{
			m_queryReader = m_dba.searchCompounds(filters);
		}
		catch (SQLException e)
		{
			e.printStackTrace();
			m_errorDisplay.setMessage(e.getMessage());
			m_errorDisplay.display();
		}
	}

	/**
	 * Converts an array based compound to a Compound.
	 * 
	 * @param compound
	 *            The array of properties for the compound.
	 * 
	 * @param names
	 *            The array of names for the compound.
	 * 
	 * @param newCompound
	 *            If true, any byte properties will be set to empty. If false,
	 *            any byte properties will be loaded from what they are in the
	 *            database before editing.
	 * 
	 * @return The converted compound.
	 * 
	 * @throws SQLException
	 *             when an error occurs communicating with the database.
	 * 
	 * @throws NumberFormatException
	 *             if a property is unable to be parsed to its native datatype.
	 */
	private Compound convertArrayToCompound(Object[] compound, String[] names, boolean newCompound) throws SQLException, NumberFormatException
	{
		List<ColumnField> cols = m_dba.getCompoundTableColumns();
		List<String> nameList = new ArrayList<String>();

		if (names == null)
		{
			System.out.println("Null names array!");
		}
		else
		{
			for (int x = 0; x < names.length; x++)
			{
				nameList.add(names[x]);
			}
		}
		Compound comp = new Compound(0, 0);
		comp.setNames(nameList);
		for (int i = 0; i < cols.size(); i++)
		{
			comp.setProperty(cols.get(i).getName(), parseProperty(cols.get(i), compound[i + 1]));
		}

		Compound oldComp = null;
		if (!newCompound)
		{
			List<Long> guids = new ArrayList<Long>();
			guids.add(comp.getGUID());
			QueryReader rdr = m_dba.getCompounds(guids);
			List<Compound> comps = rdr.getCompounds(1, 1);
			if (comps.size() <= 0)
			{
				throw new SQLException("Given compound guid does not exist in the database.");
			}

			oldComp = comps.get(0);
		}
		for (int i = 0; i < cols.size(); i++)
		{
			if (cols.get(i).getDataType() == DataType.BYTES)
			{
				if (newCompound)
				{
					comp.setProperty(cols.get(i).getName(), null);
				}
				else
				{
					comp.setProperty(cols.get(i).getName(), oldComp.getProperty(cols.get(i).getName()));
				}
			}
		}

		return comp;
	}

	/**
	 * Converts a Compound to an array of its properties.
	 * 
	 * @param comp
	 *            The compound to convert.
	 * 
	 * @return The array of compound properties.
	 */
	private Object[] convertCompoundToArray(Compound comp)
	{
		String[] cols = getColumnTitles();

		Object[] comparray = new Object[cols.length];

		comparray[0] = Boolean.valueOf(false);
		for (int col = 1; col < cols.length; col++)
		{
			comparray[col] = comp.getProperty(cols[col]);
		}

		return comparray;
	}

	/**
	 * Converts a list of Compound into a 2d array containing the compounds'
	 * properties.
	 * 
	 * @param comps
	 *            The list of compounds to convert.
	 * 
	 * @return The array of compound properties.
	 */
	private Object[][] convertCompoundToArray(List<Compound> comps)
	{
		String[] cols = getColumnTitles();

		Object[][] comparray = new Object[comps.size()][cols.length];

		for (int comp = 0; comp < comps.size(); comp++)
		{
			comparray[comp][0] = Boolean.valueOf(false);
			for (int col = 1; col < cols.length; col++)
			{
				comparray[comp][col] = comps.get(comp).getProperty(cols[col]);
			}
		}

		return comparray;
	}

	@Override
	public void deleteColumn(String name)
	{
		List<ColumnField> cols = null;
		try
		{
			cols = m_dba.getCompoundTableColumns();
		}
		catch (SQLException e)
		{
			e.printStackTrace();
			System.out.println(e.getMessage());
			m_errorDisplay.setMessage(e.getMessage());
			m_errorDisplay.display();
			return;
		}
		ColumnField colToRemove = null;
		for (ColumnField col : cols)
		{
			if (col.getName().equals(name))
			{
				colToRemove = col;
			}
		}

		if (colToRemove == null)
		{
			return;
		}

		try
		{
			m_dba.removeColumnField(colToRemove);
		}
		catch (SQLException e)
		{
			e.printStackTrace();
			System.out.println(e.getMessage());
			m_errorDisplay.setMessage(e.getMessage());
			m_errorDisplay.display();
		}
	}

	@Override
	public void deleteCompound(List<Long> guids)
	{
		try
		{
			m_dba.deleteCompound(guids);
		}
		catch (SQLException e)
		{
			e.printStackTrace();
			System.out.println(e.getMessage());
			m_errorDisplay.setMessage(e.getMessage());
			m_errorDisplay.display();
		}
	}

	@Override
	public void editCompound(Object[] compound, String[] names)
	{
		if (names == null || names.length <= 0)
		{
			m_errorDisplay.setMessage("Must have at least one name for a compound.");
			m_errorDisplay.display();
			return;
		}
		Compound comp;
		try
		{
			comp = convertArrayToCompound(compound, names, false);
		}
		catch (NumberFormatException e)
		{
			e.printStackTrace();
			System.out.println(e.getMessage());
			m_errorDisplay.setMessage(e.getMessage());
			m_errorDisplay.display();
			return;
		}
		catch (SQLException e)
		{
			e.printStackTrace();
			System.out.println(e.getMessage());
			m_errorDisplay.setMessage(e.getMessage());
			m_errorDisplay.display();
			return;
		}

		try
		{
			m_dba.updateCompound(comp);
		}
		catch (SQLException e)
		{
			e.printStackTrace();
			System.out.println(e.getMessage());
			m_errorDisplay.setMessage(e.getMessage());
			m_errorDisplay.display();
		}
	}

	@Override
	public void exportCompounds(List<Long> guids, String filename)
	{
		System.out.println("Export to: " + filename);
		// Get exporter for file type. If not supported, let user know and
		// finish
		MoleculeWriter mw = null;
		int dotPos = filename.lastIndexOf(".");
		if (dotPos >= 0)
		{
			String extension = filename.substring(dotPos);
			mw = IOController.getWriter(extension);
		}

		if (mw == null)
		{
			m_errorDisplay.setMessage("Error: File type not supported");
			m_errorDisplay.display();
			return;
		}

		// Open file, and prepare to start exporting
		try
		{
			mw.open(filename);
			mw.seek(0);
		}
		catch (FileNotFoundException e)
		{
			e.printStackTrace();
			m_errorDisplay.setMessage(e.getMessage());
			m_errorDisplay.display();
			return;
		}
		catch (IOException e)
		{
			e.printStackTrace();
			m_errorDisplay.setMessage(e.getMessage());
			m_errorDisplay.display();
			return;
		}

		// Search compounds from database using guids, and prepare query reader
		QueryReader rdr = null;
		int numResults = 0;
		try
		{
			rdr = m_dba.getCompounds(guids);
			numResults = rdr.getCompoundCount();
			rdr.seek(1);

			if (rdr == null || numResults == 0)
			{
				m_errorDisplay.setMessage("No compounds selected or error occurred...");
				m_errorDisplay.display();
				return;
			}
		}
		catch (SQLException e)
		{
			e.printStackTrace();
			m_errorDisplay.setMessage(e.getMessage());
			m_errorDisplay.display();
			return;
		}

		// Write compounds to file
		while (true)
		{
			try
			{
				Compound comp = rdr.getCompound();
				if (comp == null)
				{
					break;
				}
				mw.writeCompound(comp);
			}
			catch (SQLException e)
			{
				e.printStackTrace();
				m_errorDisplay.setMessage(e.getMessage());
				m_errorDisplay.display();
			}
			catch (IOException e)
			{
				e.printStackTrace();
				m_errorDisplay.setMessage(e.getMessage());
				m_errorDisplay.display();
			}
		}

		m_errorDisplay.setMessage("Export completed");
		m_errorDisplay.display();

	}

	/**
	 * Closes up the query reader used to get last search results.
	 */
	private void finishReader()
	{
		if (m_queryReader == null)
		{
			return;
		}
		try
		{
			m_queryReader.close();
		}
		catch (SQLException e)
		{
			e.printStackTrace();
		}
		m_queryReader = null;
	}

	@Override
	public SearchOptionType[] getAllowedSearchOptions(String column)
	{
		List<ColumnField> cols;
		try
		{
			cols = m_dba.getCompoundTableColumns();
		}
		catch (SQLException e)
		{
			e.printStackTrace();
			m_errorDisplay.setMessage(e.getMessage());
			m_errorDisplay.display();
			return new SearchOptionType[0];
		}

		if (cols == null || cols.size() == 0)
		{
			m_errorDisplay.setMessage("Error getting allowed search options for property.");
			m_errorDisplay.display();
			return new SearchOptionType[0];
		}

		for (ColumnField col : cols)
		{
			if (column.equals(col.getName()))
			{
				switch (col.getDataType())
				{
				case STRING:
					return new SearchOptionType[] { SearchOptionType.CONTAINS, SearchOptionType.STARTS_WITH, SearchOptionType.ENDS_WITH,
							SearchOptionType.EQUALSTRING };
				case LONG:
				case INTEGER:
				case FLOAT:
				case DOUBLE:
					return new SearchOptionType[] { SearchOptionType.MIN, SearchOptionType.MAX, SearchOptionType.EQUALNUMBER };
				default:
					// Should not reach here except for BLOB, whihc cant be
					// searched on.
					return new SearchOptionType[0];
				}
			}
		}

		return new SearchOptionType[0];
	}

	@Override
	public String[] getAllowedSearchOptionsInStrings(String column)
	{
		List<ColumnField> cols;
		try
		{
			cols = m_dba.getCompoundTableColumns();
		}
		catch (SQLException e)
		{
			e.printStackTrace();
			m_errorDisplay.setMessage(e.getMessage());
			m_errorDisplay.display();
			return new String[0];
		}

		if (cols == null || cols.size() == 0)
		{
			m_errorDisplay.setMessage("Error getting allowed search options for property.");
			m_errorDisplay.display();
			return new String[0];
		}

		for (ColumnField col : cols)
		{
			if (column.equals(col.getName()))
			{
				switch (col.getDataType())
				{
				case STRING:
					return new String[] { "CONTAINS", "STARTS_WITH", "ENDS_WITH", "EQUALSTRING" };
				case LONG:
				case INTEGER:
				case FLOAT:
				case DOUBLE:
					return new String[] { "RANGE", "MIN", "MAX", "EQUALNUMBER" };
				default:
					// Should not reach here except for BLOB, whihc cant be
					// searched on.
					return new String[0];
				}
			}
		}

		return new String[0];
	}

	@Override
	public ColumnField getColumnField(String name)
	{
		List<ColumnField> cols;
		try
		{
			cols = m_dba.getCompoundTableColumns();
		}
		catch (SQLException e)
		{
			e.printStackTrace();
			m_errorDisplay.setMessage(e.getMessage());
			m_errorDisplay.display();
			return null;
		}

		if (cols == null || cols.size() == 0)
		{
			m_errorDisplay.setMessage("Error getting allowed search options for property.");
			m_errorDisplay.display();
			return null;
		}

		for (ColumnField col : cols)
		{
			if (name.equals(col.getName()))
			{
				return col;
			}
		}
		return null;
	}

	/**
	 * Gets a given compound name from the database.
	 * 
	 * @param name
	 *            The name of the property/column to check.
	 * 
	 * @return The compound column field if it exists, else null.
	 * @throws SQLException
	 *             when an error occurs communicating with the database, or an
	 *             empty column list is received from the database accessor.
	 */
	private ColumnField getColumnFromDatabase(String name) throws SQLException
	{
		List<ColumnField> cols = null;
		cols = m_dba.getCompoundTableColumns();
		if (cols == null)
		{
			throw new SQLException("Error: empty list of properties.");
		}

		for (ColumnField col : cols)
		{
			if (col.getName().equals(name))
			{
				return col;
			}
		}

		return null;
	}

	/**
	 * Gets the generic set of properties for compounds.
	 * 
	 * @param addSelectColumn
	 *            Determines whether or not a column of Title "Select" will be
	 *            added as the first column or not.
	 * 
	 * @return A string array with the properties columns.
	 * 
	 * @throws SQLException
	 *             when an error occurs communicating with the database, or an
	 *             empty list is received from the database.
	 */
	private String[] getColumnNames(boolean addSelectColumn) throws SQLException
	{
		List<ColumnField> cols = null;
		cols = m_dba.getCompoundTableColumns();

		if (cols == null)
		{
			throw new SQLException("Error: empty list of properties.");
		}

		String[] names = new String[cols.size()];
		int offset = 0;
		if (addSelectColumn)
		{
			names = new String[cols.size() + 1];
			names[0] = "Select";
			offset = 1;
		}

		for (int x = offset; x < names.length; x++)
		{
			names[x] = cols.get(x - offset).getName();
		}

		return names;
	}

	@Override
	public String[] getColumnTitles()
	{
		try
		{
			return getColumnNames(true);
		}
		catch (SQLException e)
		{
			e.printStackTrace();
			System.out.println(e.getMessage());
			m_errorDisplay.setMessage(e.getMessage());
			m_errorDisplay.display();
		}
		return new String[0];
	}

	@Override
	public Object[] getCompound(long guid)
	{
		List<Long> guids = new ArrayList<Long>();
		guids.add(guid);

		try
		{
			QueryReader rdr = m_dba.getCompounds(guids);
			List<Compound> comps = rdr.getCompounds(1, 1);

			if (comps.size() <= 0)
			{
				return new Object[0];
			}

			return convertCompoundToArray(comps.get(0));

		}
		catch (SQLException e)
		{
			e.printStackTrace();
			m_errorDisplay.setMessage(e.getMessage());
			m_errorDisplay.display();
		}

		return new Object[0];
	}

	@Override
	public String[] getCompoundNames(long guid)
	{
		List<String> nameList = null;
		try
		{
			nameList = m_dba.getNames(guid);
		}
		catch (SQLException e)
		{
			e.printStackTrace();
			System.out.println(e.getMessage());
			m_errorDisplay.setMessage(e.getMessage());
			m_errorDisplay.display();
		}

		if (nameList == null)
		{
			return new String[0];
		}

		String[] names = new String[nameList.size()];
		for (int x = 0; x < names.length; x++)
		{
			names[x] = nameList.get(x);
		}
		return names;
	}

	@Override
	public int getSearchResultCount()
	{
		if (m_queryReader == null)
		{
			return 0;
		}
		int count = 0;

		try
		{
			count = m_queryReader.getCompoundCount();
		}
		catch (SQLException e)
		{
			e.printStackTrace();
			m_errorDisplay.setMessage(e.getMessage());
			m_errorDisplay.display();
			return 0;
		}

		return count;
	}

	@Override
	public Object[][] getSearchResults(int resultNumStart, int numResults)
	{

		List<Compound> compList = null;
		try
		{
			compList = m_queryReader.getCompounds(resultNumStart, numResults);
		}
		catch (SQLException e)
		{
			e.printStackTrace();
			m_errorDisplay.setMessage(e.getMessage());
			m_errorDisplay.display();
		}

		Object[][] compArray;

		if (compList == null)
		{
			return new Object[0][0];
		}

		compArray = convertCompoundToArray(compList);

		return compArray;
	}

	@Override
	public void importCompounds(String filename)
	{
		// Make sure import dialog is ready
		if (m_importDisplay == null)
		{
			m_errorDisplay.setMessage("Error: Import dialog not initialized...");
			m_errorDisplay.display();
			return;
		}

		// Get importer for file type. If not supported, let user know and
		// finish
		MoleculeReader mr = null;
		int dotPos = filename.lastIndexOf(".");
		if (dotPos >= 0)
		{
			String extension = filename.substring(dotPos);
			mr = IOController.getReader(extension);
		}

		if (mr == null)
		{
			m_errorDisplay.setMessage("Error: File type not supported");
			m_errorDisplay.display();
			return;
		}

		// Open file, and prepare to start importing
		try
		{
			mr.open(filename);
			mr.seek(0);
		}
		catch (FileNotFoundException e)
		{
			e.printStackTrace();
			m_errorDisplay.setMessage(e.getMessage());
			m_errorDisplay.display();
			return;
		}
		catch (IOException e)
		{
			e.printStackTrace();
			m_errorDisplay.setMessage(e.getMessage());
			m_errorDisplay.display();
			return;
		}

		// List to store compounds in, and map to use in processing raw import
		// properties
		List<Compound> comps = new ArrayList<Compound>();
		Map<String, String> propMap = new HashMap<String, String>();

		boolean doneImporting = false;

		// Iterate through compounds, reading, processing and entering into the
		// database IMPORTS_PER_ITERATION at a time.
		while (!doneImporting)
		{
			try
			{
				Compound curr = null;

				// Read in compounds from file, processing each one before
				// adding it to the list.
				for (int count = 0; count < IMPORTS_PER_ITERATION && !doneImporting; count++)
				{
					try
					{
						curr = mr.readCompound();
					}
					catch (IOException e)
					{
						if (e.getMessage().equals("No more compounds"))
						{
							doneImporting = true;
							break;
						}
						else
						{
							throw e;
						}
					}

					if (curr == null)
					{
						doneImporting = true;
						break;
					}

					try
					{
						mapCompound(curr, propMap);
						curr.setGUID(0);
					}
					catch (SQLException e)
					{
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					comps.add(curr);
				}

			}
			catch (IOException e)
			{
				e.printStackTrace();
				System.out.println(e.getMessage());
				m_errorDisplay.setMessage(e.getMessage());
				m_errorDisplay.display();
			}

			// Progress update for user
			// TODO: Print to a status log or something that the user can see...
			// console will do for now
			System.out.println(comps.size() + " compounds processed, now entering into database...");

			// Add list of compounds to the database.
			try
			{
				m_dba.addCompounds(comps);
			}
			catch (SQLException e)
			{
				e.printStackTrace();
				System.out.println(e.getMessage());
				m_errorDisplay.setMessage(e.getMessage());
				m_errorDisplay.display();
			}

			// Progress update for user
			// TODO: Print to a status log or something that the user can see...
			// console will do for now
			System.out.println(comps.size() + " compounds imported into the database.");
		}

		if (mr.isOpen())
		{
			mr.close();
		}

		// Completion update for user
		m_errorDisplay.setMessage("All compounds imported into the database.");
		m_errorDisplay.display();
	}

	/**
	 * Maps a compound to existing properties during import using the given map
	 * so far.
	 * 
	 * @param compound
	 *            The newly imported compound to map (will be updated).
	 * 
	 * @param map
	 *            The map for the import so far (will be updated here if
	 *            necessary).
	 * 
	 * @throws SQLException
	 *             when an error occurs communicating with the database, or
	 */
	private void mapCompound(Compound compound, Map<String, String> map) throws SQLException
	{
		Map<String, Object> propsImported = compound.getProperties();
		Map<String, Object> propsProcessed = new HashMap<String, Object>();

		for (String propName : propsImported.keySet())
		{
			Object prop = propsImported.get(propName);

			// If we havnt processed this property before, then we need to
			// process it and add it to the map.
			if (!map.containsKey(propName))
			{
				boolean doneMapping = false;
				boolean triedDirectMatch = false;
				while (!doneMapping)
				{
					ColumnField sel = null;

					// Try matching it to an existing column with .equals
					if (!triedDirectMatch)
					{
						sel = getColumnFromDatabase(propName);
						triedDirectMatch = true;
					}

					// Direct match didn't work, get user input
					if (sel == null)
					{
						String[] propMaps = getColumnNames(false);
						m_importDisplay.setPropertiesMaps(propMaps);
						m_importDisplay.setProperty(propName);
						m_importDisplay.display();
						String candProp = m_importDisplay.getPropertyMap();

						// If user is creating a new column
						if (m_importDisplay.isNewcolumn())
						{
							// Check new column will work before wasting time
							// setting it up
							DataType dt = m_importDisplay.getNewColumnDatatype();
							ColumnField col = new ColumnField(candProp, dt, 0);
							try
							{
								parseProperty(col, prop);
								m_dba.addColumnField(candProp, col.getDataType());
							}
							catch (NumberFormatException e)
							{
								m_errorDisplay.setMessage(propName + " data type did not match " + col.getName());
								m_errorDisplay.display();
							}
							catch (SQLException e)
							{
								m_errorDisplay.setMessage(e.getMessage());
								m_errorDisplay.display();
							}
						}

						sel = getColumnFromDatabase(candProp);
						if (sel == null)
						{
							m_errorDisplay.setMessage("Property not found in the database.");
							m_errorDisplay.display();
						}
					}

					// We have a possible column, so check the data matches. If
					// not, show error and loop (try) again.
					if (sel != null)
					{
						try
						{
							parseProperty(sel, prop);
							map.put(propName, sel.getName());
							doneMapping = true;
						}
						catch (NumberFormatException e)
						{
							m_errorDisplay.setMessage(propName + " data type did not match " + sel.getName());
							m_errorDisplay.display();
						}
					}
				}
			}

			String mapProp = map.get(propName);
			propsProcessed.put(mapProp, prop);
		}

		// Finally, update the compound with mapped properties.
		compound.setProperties(propsProcessed);
	}

	/**
	 * Converts a property (if of type string) to its appropriate data type.
	 * 
	 * @param column
	 *            The ColumnField of the property.
	 * 
	 * @param property
	 *            The property to parse.
	 * 
	 * @return The parsed object in its proper data type.
	 * 
	 * @throws NumberFormatException
	 *             if a property is unable to be parsed to its native datatype.
	 */
	private Object parseProperty(ColumnField column, Object property) throws NumberFormatException
	{
		if (!(property instanceof String))
		{
			return property;
		}
		if (((String) property).length() == 0)
		{
			return null;
		}
		try
		{
			switch (column.getDataType())
			{
			case STRING:
				return property;
			case LONG:
				return Long.parseLong((String) property);
			case INTEGER:
				return Integer.parseInt((String) property);
			case FLOAT:
				return Float.parseFloat((String) property);
			case DOUBLE:
				return Double.parseDouble((String) property);
			case BYTES:
				// Bytes should be dealt with else where, just return null
				return null;
			default:
				// Should not reach here.
				throw new NumberFormatException();
			}
		}
		catch (NumberFormatException exc)
		{
			throw new NumberFormatException("Input error for column: " + column.getName() + ", value: " + property.toString());
		}
	}

	@Override
	public void quickSearch(String query)
	{
		finishReader();

		// Ungraceful, but to help stop SQL injections
		query = query.replace('\'', ' ');

		try
		{
			m_queryReader = m_dba.getCompounds(query);
		}
		catch (SQLException e)
		{
			e.printStackTrace();
			m_errorDisplay.setMessage(e.getMessage());
			m_errorDisplay.display();
		}
	}

	@Override
	public void renameColumn(String name, String newName)
	{
		List<ColumnField> cols = null;
		try
		{
			cols = m_dba.getCompoundTableColumns();
		}
		catch (SQLException e)
		{
			e.printStackTrace();
			System.out.println(e.getMessage());
			m_errorDisplay.setMessage(e.getMessage());
			m_errorDisplay.display();
			return;
		}
		ColumnField colToRemove = null;
		for (ColumnField col : cols)
		{
			if (col.getName().equals(name))
			{
				colToRemove = col;
			}
		}

		if (colToRemove == null)
		{
			return;
		}

		try
		{
			m_dba.renameColumnField(colToRemove, newName);
		}
		catch (SQLException e)
		{
			e.printStackTrace();
			System.out.println(e.getMessage());
			m_errorDisplay.setMessage(e.getMessage());
			m_errorDisplay.display();
		}
	}

	@Override
	public void setErrorDialogBox(ErrorUserDialog dialog)
	{
		m_errorDisplay = dialog;
	}

	@Override
	public void setImportDialogBox(ImportUserDialog dialog)
	{
		m_importDisplay = dialog;
	}

}
